# base working
library(dplyr)
library(tidyverse)
library(readr)
library(ggplot2)
library(dashHtmlComponents)
library(dashCoreComponents)
library(plotly)
library(dash)
library(purrr)
suf_data <- read.csv(file = 'data/processed/sufang_clean_df.csv')
j_data <- read_csv("data/processed/jasmine_df.csv")
New names:
* `` -> ...1
Rows: 3939 Columns: 7
── Column specification ──────────────────────────────────────────────────────────────────────────────────────────────────
Delimiter: ","
chr (4): title, cast, listed_in, rating
dbl (3): ...1, cast_count, release_year
ℹ Use `spec()` to retrieve the full column specification for this data.
ℹ Specify the column types or set `show_col_types = FALSE` to quiet this message.
data <- read_csv("data/processed/clean_df.csv")
New names:
* `` -> ...1
Rows: 3939 Columns: 13
── Column specification ──────────────────────────────────────────────────────────────────────────────────────────────────
Delimiter: ","
chr (10): title, director, cast, country, date_added, rating, duration, listed_in, description, type
dbl (3): ...1, show_id, release_year
ℹ Use `spec()` to retrieve the full column specification for this data.
ℹ Specify the column types or set `show_col_types = FALSE` to quiet this message.
j_data <- j_data %>% filter(is.na(j_data$cast_count) == FALSE)
cast_data <- j_data %>%
group_by(release_year) %>%
summarize(mean_cast_count = mean(cast_count))
p <- cast_data %>%
ggplot(aes(x=release_year,
y=mean_cast_count)) +
geom_point(fill= "red2", color = "black", shape = 21, size = 3) +
ggtitle("Average Cast Size Per Year") +
xlab("Release Year") +
ylab("Avg. Cast Size") +
xlim(1942, 2020) +
theme(plot.title = element_text(hjust = 0.5, color = "white"),
panel.background = element_blank(),
panel.grid = element_line(color = "gray90"),
axis.line = element_line(colour = "black"),
plot.background = element_rect(fill = '#171614', colour = '#171614'),
axis.text = element_text(color="white"),
axis.title.x = element_text(color="white"),
axis.title.y = element_text(color="white")
)
ggplotly(p + aes(text = release_year), tooltip = 'release_year') %>% layout(plot_bgcolor = '000000')
c("#CE2626",
"#820263",
"#FBBA72",
"#EFAAC4",
"#A56124",
"#D30C7B",
"#520F2A",
"#FF3C38",
"#FF8C42",
"#F991CC",
"#A05BFA",
"#9F2042")
summarize(mean_cast_count = mean(cast_count))
app <- Dash$new(external_stylesheets = dbcThemes$BOOTSTRAP)
app$layout(
dbcContainer(
list(
# title card
dbcRow(
list(
dbcCard(
dbcCardBody(
list(h4("Netflix Movie Dashboard: Visualize movie trends on the world's most popular streaming platform!", className = "card-title")),
dbcCol(
style=list("font-weight"="bold", "font-size"="85%"),
), # dbcCol
), # dbcCardBody
color ="dark",
inverse=TRUE
) #dbcCard
), # list
), # dbcRow
# first row
dbcRow(
list(
# Jasmine's plot
dbcCol(
dbcCard(
dbcCardBody(
div(
list(
dccGraph(id='scatter'),
htmlLabel('Year Range'),
dccSlider(
id='xslider',
min=1942,
max=2019,
marks = list(
'1942' = '1942',
'1962' = '1962',
'1982' = '1982',
'2002' = '2002',
'2019'= '2019'
),
value=2002)
)
) # html
), # dbcCardBody
color="dark"
), # dbcCard
md=6,
), # Jasmine dbcCol
# Mahsa plot
dbcCol(
dbcCard(
dbcCardBody(
div(
list(
dccGraph(id='plot_line'),
dccDropdown(
id='rating-select',
options = data$rating %>%
purrr::map(function(rating,pop) list(label = rating, value = rating)),
value=list("TV-G","TV-MA", "TV-14","TV-Y7"),
multi=TRUE
),
dccRangeSlider(
id='my-range-slider',
min=1942,
max=2020,
marks =
list(
'1942' = '1942',
'1960' = '1960',
'1980' = '1980',
'2000' = '2000',
'2020' = '2020'
),
value=list(2003, 2020)
)
) # list
) # div
), # dbcCardBody
color="dark"
), # dbcCard
md=6), # close dbcCol
dbcRow(
dbcCol(
dbcCard(
dbcCardBody(
div(
##
list(
dccGraph(id='plot-area'),
htmlLabel("Year"),
dccRangeSlider(id='year3',
min = min(suf_data$release_year),
max= max(suf_data$release_year),
value=list(1995, 2020),
marks=list(
'1970'= "1970",
'1975'= "1975",
'1980'= "1980",
'1985'= "1985",
'1990'= "1990",
'1995'= "1995",
'2000'= "2000",
'2005'= "2005",
'2010'= "2010",
'2015'= "2015",
'2020'= "2020"
)
),
htmlLabel("Duration"),
dccRangeSlider(id='duration3',
min = min(suf_data$duration),
max = max(suf_data$duration),
value=list(60, 120),
marks=list(
'10'= "10 min",
'30'= "30 min",
'50'= "50 min",
'70'= "70 min",
'90'= "90 min",
'110'= "110 min",
'130'= "130 min",
'150'= "150 min",
'170'= "170 min",
'190'= "190 min",
'210'= "210 min",
'230'= "230 min")
)
)
###
) # div
), # dbcCardBody
color="dark"
) # dbcCard
)
)
) # close list
) # first row
), # close list
style=list(backgroundColor = "#000000")
) # container
)
# jasmine callback
app$callback(
output('scatter', 'figure'),
list(input('xslider', 'value')),
function(xcol) {
p <- cast_data %>%
ggplot(aes(x=release_year,
y=mean_cast_count)) +
geom_point(fill= "red2", color = "black", shape = 21, size = 3) +
ggtitle("Average Cast Size Per Year") +
xlab("Release Year") +
ylab("Avg. Cast Size") +
xlim(1942, xcol) +
theme(plot.title = element_text(hjust = 0.5),
panel.background = element_blank(),
panel.grid = element_line(color = "gray90"),
axis.line = element_line(colour = "black"),
plot.background = element_rect(fill = '#171614', colour = '#171614')
)
ggplotly(p + aes(text = release_year), tooltip = 'release_year') %>% layout(plot_bgcolor = '000000')
}
)
# mahsa
app$callback(
output('plot_line', 'figure'),
list(input('rating-select', 'value'),
input('my-range-slider','value')),
function(ratings_range,year_range) {
df <- na.omit(data) %>%
filter(release_year > year_range[1],release_year < year_range[2]) %>%
filter(rating %in% ratings_range) %>%
group_by(release_year,rating) %>%
summarise(count = length(rating))
plot <- ggplot(df ,aes(x = release_year, y = count, color = rating)) +
geom_line()+
scale_size(range = c(2, 12)) +
ggtitle('Movie rating in Netflix in different years') +
labs(x = 'Years', y= "Number of movie") +
theme_bw() +
theme(text = element_text(size = 10)) +
ggthemes::scale_color_tableau()
ggplotly(plot)
}
)
# sufang callback
app$callback(
output('plot-area', 'figure'),
list(input('year3', 'value'),
input('duration3', 'value')),
function(year_range,duration_range){
data_filt <- suf_data %>%
dplyr::filter((release_year > year_range[1] & release_year < year_range[2]) & (duration > duration_range[1] & duration < duration_range[2]))
p1 <- ggplot(data_filt, aes(x = forcats::fct_infreq(country)),text = name) +
geom_bar(stat = 'count', color = "black", fill = "red2") +
ggtitle('Which Countries Make the Most Movies?') +
labs(x = 'Country') +
labs(y = 'Number of Movies Produced')+
theme_classic() +
ggthemes::scale_color_tableau()+
theme(axis.text.x = element_text(angle = 90, vjust = 0.5, hjust=1))
ggplotly(p1)
}
)
# app$run_server(host = '0.0.0.0')
app$run_server(debug = T) # use when running locally
# work on
library(dash)
library(dashCoreComponents)
Attaching package: ‘dashCoreComponents’
The following objects are masked from ‘package:dash’:
dccChecklist, dccConfirmDialog, dccConfirmDialogProvider, dccDatePickerRange, dccDatePickerSingle,
dccDropdown, dccGraph, dccInput, dccInterval, dccLink, dccLoading, dccLocation, dccLogoutButton,
dccMarkdown, dccRadioItems, dccRangeSlider, dccSlider, dccStore, dccTab, dccTabs, dccTextarea, dccUpload
library(ggplot2)
library(plotly)
Registered S3 method overwritten by 'data.table':
method from
print.data.table
Registered S3 method overwritten by 'htmlwidgets':
method from
print.htmlwidget tools:rstudio
Attaching package: ‘plotly’
The following object is masked from ‘package:ggplot2’:
last_plot
The following object is masked from ‘package:stats’:
filter
The following object is masked from ‘package:graphics’:
layout
library(purrr)
# drop down list
drop_list <- list(
"TV-G",
"TV-14",
"TV-MA",
"TV-PG",
"R",
"TV-Y7",
"TV-Y",
"PG",
"G",
"PG-13",
"NR",
"UR",
"TV-Y7-FV",
"NC-17")
data = read.csv(file = "data/processed/clean_df.csv")
app <- Dash$new(external_stylesheets = dbcThemes$BOOTSTRAP)
app$layout(
dbcContainer(
list(
dccGraph(id='plot_line'),
dccDropdown(
id='rating-select',
options = drop_list %>% purrr::map(function(rating,pop) list(label = rating, value = rating)) ,
value=list("TV-G","TV-MA", "TV-14","TV-Y7"),
multi=TRUE),
dccRangeSlider(
id='my-range-slider',
min=1942,
max=2020,
marks = list(
'1942' = '1942',
'1960' = '1960',
'1980' = '1980',
'2000' = '2000',
'2020' = '2020'
),
value=list(2003, 2020)
)
)
)
)
app$callback(
output('plot_line', 'figure'),
list(input('rating-select', 'value'),
input('my-range-slider','value')),
function(ratings_range,year_range) {
df <- na.omit(data) %>%
filter(release_year > year_range[1],release_year < year_range[2]) %>%
filter(rating %in% ratings_range) %>%
group_by(release_year,rating) %>%
summarise(count = length(rating))
plot <- ggplot(df ,aes(x = release_year, y = count, color = rating)) +
geom_line()+
scale_size(range = c(2, 12)) +
ggtitle('Movie rating in Netflix in different years') +
labs(x = 'Years', y= "Number of movie") +
theme_bw() +
theme(text = element_text(size = 10)) +
ggthemes::scale_color_tableau()
ggplotly(plot)
}
)
app$run_server(debug = T) # use when running locally
⚠ No source directory information available; hot reloading has been disabled.
Please ensure that you are loading your Dash for R application using source().
⚠ Note: As of version 1.0, the following packages are deprecated and should no longer be installed or loaded
when using Dash for R: `dashHtmlComponents`, `dashCoreComponents`, `dashTable`. These components are now
bundled within the `dash` package.
Fire started at 127.0.0.1:8050
start: 127.0.0.1:8050
NA
stop: 127.0.0.1:8050
drop_list <- list("TV-G",
"TV-14",
"TV-MA"
)
options <- drop_list %>% purrr::map(function(rating,pop) list(label = rating, value = rating))
options
[[1]]
[[1]]$label
[1] "TV-G"
[[1]]$value
[1] "TV-G"
[[2]]
[[2]]$label
[1] "TV-14"
[[2]]$value
[1] "TV-14"
[[3]]
[[3]]$label
[1] "TV-MA"
[[3]]$value
[1] "TV-MA"
# testing <- data |>
# select(rating, title) |>
# group_by(rating)
#
#
# str(data$rating)
# drop_list <- list("TV-G",
# "TV-14",
# "TV-MA"
# )
LS0tCnRpdGxlOiAiUiBOb3RlYm9vayIKb3V0cHV0OiBodG1sX25vdGVib29rCi0tLQoKYGBge3J9CiMgYmFzZSB3b3JraW5nCgpsaWJyYXJ5KGRwbHlyKQpsaWJyYXJ5KHRpZHl2ZXJzZSkKbGlicmFyeShyZWFkcikKbGlicmFyeShnZ3Bsb3QyKQpsaWJyYXJ5KGRhc2hIdG1sQ29tcG9uZW50cykKbGlicmFyeShkYXNoQ29yZUNvbXBvbmVudHMpCmxpYnJhcnkocGxvdGx5KQpsaWJyYXJ5KGRhc2gpCmxpYnJhcnkocHVycnIpCgpzdWZfZGF0YSA8LSByZWFkLmNzdihmaWxlID0gJ2RhdGEvcHJvY2Vzc2VkL3N1ZmFuZ19jbGVhbl9kZi5jc3YnKQpqX2RhdGEgPC0gcmVhZF9jc3YoImRhdGEvcHJvY2Vzc2VkL2phc21pbmVfZGYuY3N2IikKZGF0YSA8LSByZWFkX2NzdigiZGF0YS9wcm9jZXNzZWQvY2xlYW5fZGYuY3N2IikKCmpfZGF0YSA8LSBqX2RhdGEgJT4lIGZpbHRlcihpcy5uYShqX2RhdGEkY2FzdF9jb3VudCkgPT0gRkFMU0UpCgpjYXN0X2RhdGEgPC0gal9kYXRhICU+JSAKICBncm91cF9ieShyZWxlYXNlX3llYXIpICU+JQogIHN1bW1hcml6ZShtZWFuX2Nhc3RfY291bnQgPSBtZWFuKGNhc3RfY291bnQpKQpgYGAKCmBgYHtyfQogcCA8LSBjYXN0X2RhdGEgJT4lCiAgICAgIGdncGxvdChhZXMoeD1yZWxlYXNlX3llYXIsCiAgICAgICAgICAgICAgICAgeT1tZWFuX2Nhc3RfY291bnQpKSArCiAgICAgIGdlb21fcG9pbnQoZmlsbD0gInJlZDIiLCBjb2xvciA9ICJibGFjayIsIHNoYXBlID0gMjEsIHNpemUgPSAzKSArCiAgICAgIGdndGl0bGUoIkF2ZXJhZ2UgQ2FzdCBTaXplIFBlciBZZWFyIikgKwogICAgICB4bGFiKCJSZWxlYXNlIFllYXIiKSArCiAgICAgIHlsYWIoIkF2Zy4gQ2FzdCBTaXplIikgKwogICAgICB4bGltKDE5NDIsIDIwMjApICsKICAgICAgdGhlbWUocGxvdC50aXRsZSA9IGVsZW1lbnRfdGV4dChoanVzdCA9IDAuNSwgY29sb3IgPSAid2hpdGUiKSwKICAgICAgICAgICAgcGFuZWwuYmFja2dyb3VuZCA9IGVsZW1lbnRfYmxhbmsoKSwKICAgICAgICAgICAgcGFuZWwuZ3JpZCA9IGVsZW1lbnRfbGluZShjb2xvciA9ICJncmF5OTAiKSwKICAgICAgICAgICAgYXhpcy5saW5lID0gZWxlbWVudF9saW5lKGNvbG91ciA9ICJibGFjayIpLAogICAgICAgICAgICBwbG90LmJhY2tncm91bmQgPSBlbGVtZW50X3JlY3QoZmlsbCA9ICcjMTcxNjE0JywgY29sb3VyID0gJyMxNzE2MTQnKSwKICAgICAgICAgICAgYXhpcy50ZXh0ID0gZWxlbWVudF90ZXh0KGNvbG9yPSJ3aGl0ZSIpLAogICAgICAgICAgICBheGlzLnRpdGxlLnggPSBlbGVtZW50X3RleHQoY29sb3I9IndoaXRlIiksCiAgICAgICAgICAgIGF4aXMudGl0bGUueSA9IGVsZW1lbnRfdGV4dChjb2xvcj0id2hpdGUiKQogICAgICApCiAgICBnZ3Bsb3RseShwICsgYWVzKHRleHQgPSByZWxlYXNlX3llYXIpLCB0b29sdGlwID0gJ3JlbGVhc2VfeWVhcicpICAlPiUgbGF5b3V0KHBsb3RfYmdjb2xvciA9ICcwMDAwMDAnKQpgYGAKYGBge3J9CmMoIiNDRTI2MjYiLCAKICAiIzgyMDI2MyIsIAogICIjRkJCQTcyIiwgCiAgIiNFRkFBQzQiLCAKICAiI0E1NjEyNCIsIAogICIjRDMwQzdCIiwKICAiIzUyMEYyQSIsIAogICIjRkYzQzM4IiwgCiAgIiNGRjhDNDIiLCAKICAiI0Y5OTFDQyIsIAogICIjQTA1QkZBIiwgCiAgIiM5RjIwNDIiKQpgYGAKCmBgYHtyfQpzdW1tYXJpemUobWVhbl9jYXN0X2NvdW50ID0gbWVhbihjYXN0X2NvdW50KSkKCmFwcCA8LSBEYXNoJG5ldyhleHRlcm5hbF9zdHlsZXNoZWV0cyA9IGRiY1RoZW1lcyRCT09UU1RSQVApCgoKYXBwJGxheW91dCgKICBkYmNDb250YWluZXIoCiAgICBsaXN0KAogICAgCiAgICAjIHRpdGxlIGNhcmQgCiAgICBkYmNSb3coCiAgICAgIGxpc3QoCiAgICAgICAgZGJjQ2FyZCgKICAgICAgICAgIGRiY0NhcmRCb2R5KAogICAgICAgICAgICBsaXN0KGg0KCJOZXRmbGl4IE1vdmllIERhc2hib2FyZDogVmlzdWFsaXplIG1vdmllIHRyZW5kcyBvbiB0aGUgd29ybGQncyBtb3N0IHBvcHVsYXIgc3RyZWFtaW5nIHBsYXRmb3JtISIsIGNsYXNzTmFtZSA9ICJjYXJkLXRpdGxlIikpLAogICAgICAgICAgICBkYmNDb2woCiAgICAgICAgICAgIHN0eWxlPWxpc3QoImZvbnQtd2VpZ2h0Ij0iYm9sZCIsICJmb250LXNpemUiPSI4NSUiKSwKICAgICAgICAgICAgKSwgIyBkYmNDb2wKICAgICAgICAgICksICMgZGJjQ2FyZEJvZHkKICAgICAgICAgIGNvbG9yID0iZGFyayIsIAogICAgICAgICAgaW52ZXJzZT1UUlVFCiAgICAgICAgKSAjZGJjQ2FyZAogICAgKSwgIyBsaXN0CiAgICApLCAjIGRiY1JvdwogICAgCiAgICAjIGZpcnN0IHJvdwogICAgIGRiY1JvdygKICAgICAgbGlzdCgKICAgICAgCiAgICAgICAjIEphc21pbmUncyBwbG90CiAgICAgICAgIGRiY0NvbCggCiAgICAgICAgICBkYmNDYXJkKAogICAgICAgICAgICBkYmNDYXJkQm9keSgKICAgICAgICAgICAgICBkaXYoCiAgICAgICAgICAgICAgICAgbGlzdCgKICAgICAgICAgICAgICAgICAgZGNjR3JhcGgoaWQ9J3NjYXR0ZXInKSwKICAgICAgICAgICAgICAgICAgaHRtbExhYmVsKCdZZWFyIFJhbmdlJyksCiAgICAgICAgICAgICAgICAgIAogICAgICAgICAgICAgICAgICBkY2NTbGlkZXIoCiAgICAgICAgICAgICAgICAgICAgaWQ9J3hzbGlkZXInLAogICAgICAgICAgICAgICAgICAgIG1pbj0xOTQyLAogICAgICAgICAgICAgICAgICAgIG1heD0yMDE5LAogICAgICAgICAgICAgICAgICAgIG1hcmtzID0gbGlzdCgKICAgICAgICAgICAgICAgICAgICAgICcxOTQyJyA9ICcxOTQyJywKICAgICAgICAgICAgICAgICAgICAgICcxOTYyJyA9ICcxOTYyJywKICAgICAgICAgICAgICAgICAgICAgICcxOTgyJyA9ICcxOTgyJywKICAgICAgICAgICAgICAgICAgICAgICcyMDAyJyA9ICcyMDAyJywKICAgICAgICAgICAgICAgICAgICAgICcyMDE5Jz0gJzIwMTknCiAgICAgICAgICAgICAgICAgICAgICApLAogICAgICAgICAgICAgICAgICB2YWx1ZT0yMDAyKQogICAgICAgICAgICAgICkKICAgICAgICAgICAgICAKICAgICAgICAgICAgKSAjIGh0bWwKICAgICAgICAgICksICMgZGJjQ2FyZEJvZHkKICAgICAgICBjb2xvcj0iZGFyayIKICAgICAgICApLCAjIGRiY0NhcmQKICAgICAgICBtZD02LAogICAgICApLCAjIEphc21pbmUgZGJjQ29sCiAgICAgIAogICAgICAjIE1haHNhIHBsb3QKICAgICAgCiAgICAgIGRiY0NvbCgKICAgICAgICAgIGRiY0NhcmQoCiAgICAgICAgICAgIGRiY0NhcmRCb2R5KAogICAgICAgICAgICAgIGRpdigKICAgICAgICAgICAgICAgIGxpc3QoCiAgICAgICAgICAgICAgICAgIGRjY0dyYXBoKGlkPSdwbG90X2xpbmUnKSwKCiAgICAgICAgICAgICAgICAgIGRjY0Ryb3Bkb3duKAogICAgICAgICAgICAgICAgICAgIGlkPSdyYXRpbmctc2VsZWN0JywKICAgICAgICAgICAgICAgICAgICBvcHRpb25zID0gZGF0YSRyYXRpbmcgJT4lCiAgICAgICAgICAgICAgICAgICAgICBwdXJycjo6bWFwKGZ1bmN0aW9uKHJhdGluZyxwb3ApIGxpc3QobGFiZWwgPSByYXRpbmcsIHZhbHVlID0gcmF0aW5nKSksCiAgICAgICAgICAgICAgICAgICAgdmFsdWU9bGlzdCgiVFYtRyIsIlRWLU1BIiwgIlRWLTE0IiwiVFYtWTciKSwKICAgICAgICAgICAgICAgICAgICBtdWx0aT1UUlVFCiAgICAgICAgICAgICAgICAgICAgKSwKCiAgICAgICAgICAgICAgICAgIGRjY1JhbmdlU2xpZGVyKAogICAgICAgICAgICAgICAgICAgIGlkPSdteS1yYW5nZS1zbGlkZXInLAogICAgICAgICAgICAgICAgICAgIG1pbj0xOTQyLAogICAgICAgICAgICAgICAgICAgIG1heD0yMDIwLAogICAgICAgICAgICAgICAgICAgIG1hcmtzID0KICAgICAgICAgICAgICAgICAgICAgIGxpc3QoCiAgICAgICAgICAgICAgICAgICAgICAgICcxOTQyJyA9ICcxOTQyJywKICAgICAgICAgICAgICAgICAgICAgICAgJzE5NjAnID0gJzE5NjAnLAogICAgICAgICAgICAgICAgICAgICAgICAnMTk4MCcgPSAnMTk4MCcsCiAgICAgICAgICAgICAgICAgICAgICAgICcyMDAwJyA9ICcyMDAwJywKICAgICAgICAgICAgICAgICAgICAgICAgJzIwMjAnID0gJzIwMjAnCiAgICAgICAgICAgICAgICAgICAgICApLAogICAgICAgICAgICAgICAgICAgIHZhbHVlPWxpc3QoMjAwMywgMjAyMCkKICAgICAgICAgICAgICAgICAgKQogICAgICAgICAgICAgICAgKSAjIGxpc3QKICAgICAgICAgICAgICApICMgZGl2CiAgICAgICAgICAgICksICMgZGJjQ2FyZEJvZHkKICAgICAgICAgICAgY29sb3I9ImRhcmsiCiAgICAgICAgICApLCAjIGRiY0NhcmQKICAgIG1kPTYpLCAjIGNsb3NlIGRiY0NvbAogICAgICAKICAgIGRiY1JvdygKICAgICAgZGJjQ29sKAogICAgICAgIGRiY0NhcmQoCiAgICAgICAgIGRiY0NhcmRCb2R5KAogICAgICAgICAgICAgIGRpdigKICAgICAgICAgICAgICAgIAogICAgICAgICAgICAgICAgIyMKICAgICAgICAgICAgICAgIAogICAgICBsaXN0KAogICAgICBkY2NHcmFwaChpZD0ncGxvdC1hcmVhJyksCiAgICAgIGh0bWxMYWJlbCgiWWVhciIpLAogICAgICBkY2NSYW5nZVNsaWRlcihpZD0neWVhcjMnLAogICAgICAgICAgICAgICAgbWluID0gbWluKHN1Zl9kYXRhJHJlbGVhc2VfeWVhciksCiAgICAgICAgICAgICAgICBtYXg9IG1heChzdWZfZGF0YSRyZWxlYXNlX3llYXIpLAogICAgICAgICAgICAgICAgdmFsdWU9bGlzdCgxOTk1LCAyMDIwKSwKICAgICAgICAgICAgICAgIG1hcmtzPWxpc3QoCiAgICAgICAgICAgICAgICAgICcxOTcwJz0gIjE5NzAiLAogICAgICAgICAgICAgICAgICAnMTk3NSc9ICIxOTc1IiwKICAgICAgICAgICAgICAgICAgJzE5ODAnPSAiMTk4MCIsCiAgICAgICAgICAgICAgICAgICcxOTg1Jz0gIjE5ODUiLAogICAgICAgICAgICAgICAgICAnMTk5MCc9ICIxOTkwIiwKICAgICAgICAgICAgICAgICAgJzE5OTUnPSAiMTk5NSIsCiAgICAgICAgICAgICAgICAgICcyMDAwJz0gIjIwMDAiLAogICAgICAgICAgICAgICAgICAnMjAwNSc9ICIyMDA1IiwKICAgICAgICAgICAgICAgICAgJzIwMTAnPSAiMjAxMCIsCiAgICAgICAgICAgICAgICAgICcyMDE1Jz0gIjIwMTUiLAogICAgICAgICAgICAgICAgICAnMjAyMCc9ICIyMDIwIgogICAgICAgICAgICAgICAgKQogICAgICAgICAgICAgICAgKSwKICAgICAgaHRtbExhYmVsKCJEdXJhdGlvbiIpLAogICAgICBkY2NSYW5nZVNsaWRlcihpZD0nZHVyYXRpb24zJywKICAgICAgICAgICAgICAgIG1pbiA9IG1pbihzdWZfZGF0YSRkdXJhdGlvbiksCiAgICAgICAgICAgICAgICBtYXggPSBtYXgoc3VmX2RhdGEkZHVyYXRpb24pLAogICAgICAgICAgICAgICAgdmFsdWU9bGlzdCg2MCwgMTIwKSwKICAgICAgICAgICAgICAgIG1hcmtzPWxpc3QoCiAgICAgICAgICAgICAgICAgICcxMCc9ICAiMTAgbWluIiwKICAgICAgICAgICAgICAgICAgJzMwJz0gIjMwIG1pbiIsCiAgICAgICAgICAgICAgICAgICc1MCc9ICI1MCBtaW4iLAogICAgICAgICAgICAgICAgICAnNzAnPSAiNzAgbWluIiwKICAgICAgICAgICAgICAgICAgJzkwJz0gIjkwIG1pbiIsCiAgICAgICAgICAgICAgICAgICcxMTAnPSAiMTEwIG1pbiIsCiAgICAgICAgICAgICAgICAgICcxMzAnPSAiMTMwIG1pbiIsCiAgICAgICAgICAgICAgICAgICcxNTAnPSAiMTUwIG1pbiIsCiAgICAgICAgICAgICAgICAgICcxNzAnPSAiMTcwIG1pbiIsCiAgICAgICAgICAgICAgICAgICcxOTAnPSAiMTkwIG1pbiIsCiAgICAgICAgICAgICAgICAgICcyMTAnPSAiMjEwIG1pbiIsCiAgICAgICAgICAgICAgICAgICcyMzAnPSAiMjMwIG1pbiIpCiAgICAgICAgICApCiAgICAgICAgKQogICAgICAgICAgICAgICAgCiAgICAgICAgICAgICAgICAjIyMKICAgICAgICAgICAgICApICMgZGl2CiAgICAgICAgICksICMgZGJjQ2FyZEJvZHkKICAgICAgY29sb3I9ImRhcmsiCiAgICAgICAgKSAjIGRiY0NhcmQKICAgICAgKQogICAgKQoKICAgICAgKSAjIGNsb3NlIGxpc3QKICAgICkgIyBmaXJzdCByb3cKICAgICksICMgY2xvc2UgbGlzdAogICAgc3R5bGU9bGlzdChiYWNrZ3JvdW5kQ29sb3IgPSAiIzAwMDAwMCIpCiAgKSAjIGNvbnRhaW5lciAgCikgCgojIGphc21pbmUgY2FsbGJhY2sgCmFwcCRjYWxsYmFjaygKICBvdXRwdXQoJ3NjYXR0ZXInLCAnZmlndXJlJyksCiAgbGlzdChpbnB1dCgneHNsaWRlcicsICd2YWx1ZScpKSwKICBmdW5jdGlvbih4Y29sKSB7CgogICAgcCA8LSBjYXN0X2RhdGEgJT4lCiAgICAgIGdncGxvdChhZXMoeD1yZWxlYXNlX3llYXIsCiAgICAgICAgICAgICAgICAgeT1tZWFuX2Nhc3RfY291bnQpKSArCiAgICAgIGdlb21fcG9pbnQoZmlsbD0gInJlZDIiLCBjb2xvciA9ICJibGFjayIsIHNoYXBlID0gMjEsIHNpemUgPSAzKSArCiAgICAgIGdndGl0bGUoIkF2ZXJhZ2UgQ2FzdCBTaXplIFBlciBZZWFyIikgKwogICAgICB4bGFiKCJSZWxlYXNlIFllYXIiKSArCiAgICAgIHlsYWIoIkF2Zy4gQ2FzdCBTaXplIikgKwogICAgICB4bGltKDE5NDIsIHhjb2wpICsKICAgICAgdGhlbWUocGxvdC50aXRsZSA9IGVsZW1lbnRfdGV4dChoanVzdCA9IDAuNSksCiAgICAgICAgICAgIHBhbmVsLmJhY2tncm91bmQgPSBlbGVtZW50X2JsYW5rKCksCiAgICAgICAgICAgIHBhbmVsLmdyaWQgPSBlbGVtZW50X2xpbmUoY29sb3IgPSAiZ3JheTkwIiksCiAgICAgICAgICAgIGF4aXMubGluZSA9IGVsZW1lbnRfbGluZShjb2xvdXIgPSAiYmxhY2siKSwKICAgICAgICAgICAgcGxvdC5iYWNrZ3JvdW5kID0gZWxlbWVudF9yZWN0KGZpbGwgPSAnIzE3MTYxNCcsIGNvbG91ciA9ICcjMTcxNjE0JykKICAgICAgICAgICAgCiAgICAgICkKICAgIGdncGxvdGx5KHAgKyBhZXModGV4dCA9IHJlbGVhc2VfeWVhciksIHRvb2x0aXAgPSAncmVsZWFzZV95ZWFyJykgICU+JSBsYXlvdXQocGxvdF9iZ2NvbG9yID0gJzAwMDAwMCcpCiAgfQopCgojIG1haHNhCgphcHAkY2FsbGJhY2soCiAgb3V0cHV0KCdwbG90X2xpbmUnLCAnZmlndXJlJyksCiAgbGlzdChpbnB1dCgncmF0aW5nLXNlbGVjdCcsICd2YWx1ZScpLAogICAgICAgaW5wdXQoJ215LXJhbmdlLXNsaWRlcicsJ3ZhbHVlJykpLAogIGZ1bmN0aW9uKHJhdGluZ3NfcmFuZ2UseWVhcl9yYW5nZSkgewogICAgZGYgPC0gbmEub21pdChkYXRhKSAlPiUgCiAgICAgIGZpbHRlcihyZWxlYXNlX3llYXIgPiB5ZWFyX3JhbmdlWzFdLHJlbGVhc2VfeWVhciA8IHllYXJfcmFuZ2VbMl0pICU+JQogICAgICBmaWx0ZXIocmF0aW5nICVpbiUgcmF0aW5nc19yYW5nZSkgJT4lCiAgICAgIGdyb3VwX2J5KHJlbGVhc2VfeWVhcixyYXRpbmcpICU+JSAKICAgICAgc3VtbWFyaXNlKGNvdW50ID0gbGVuZ3RoKHJhdGluZykpCiAgICAKICAgIHBsb3QgIDwtIGdncGxvdChkZiAsYWVzKHggPSByZWxlYXNlX3llYXIsIHkgPSBjb3VudCwgY29sb3IgPSByYXRpbmcpKSArCiAgICAgIGdlb21fbGluZSgpKyAgICAgIAogICAgICBzY2FsZV9zaXplKHJhbmdlID0gYygyLCAxMikpICsKICAgICAgZ2d0aXRsZSgnTW92aWUgcmF0aW5nIGluIE5ldGZsaXggaW4gZGlmZmVyZW50IHllYXJzJykgKwogICAgICBsYWJzKHggPSAnWWVhcnMnLCB5PSAiTnVtYmVyIG9mIG1vdmllIikgKwogICAgICB0aGVtZV9idygpICsKICAgICAgdGhlbWUodGV4dCA9ICBlbGVtZW50X3RleHQoc2l6ZSA9IDEwKSkgKwogICAgICBnZ3RoZW1lczo6c2NhbGVfY29sb3JfdGFibGVhdSgpIAogICAgCiAgICBnZ3Bsb3RseShwbG90KQogIH0KKQoKIyBzdWZhbmcgY2FsbGJhY2sKCmFwcCRjYWxsYmFjaygKICBvdXRwdXQoJ3Bsb3QtYXJlYScsICdmaWd1cmUnKSwKICBsaXN0KGlucHV0KCd5ZWFyMycsICd2YWx1ZScpLAogICAgICAgaW5wdXQoJ2R1cmF0aW9uMycsICd2YWx1ZScpKSwKICBmdW5jdGlvbih5ZWFyX3JhbmdlLGR1cmF0aW9uX3JhbmdlKXsKICAgIGRhdGFfZmlsdCA8LSBzdWZfZGF0YSAlPiUKICAgICAgZHBseXI6OmZpbHRlcigocmVsZWFzZV95ZWFyID4geWVhcl9yYW5nZVsxXSAmIHJlbGVhc2VfeWVhciA8IHllYXJfcmFuZ2VbMl0pICYgKGR1cmF0aW9uID4gZHVyYXRpb25fcmFuZ2VbMV0gJiBkdXJhdGlvbiA8IGR1cmF0aW9uX3JhbmdlWzJdKSkKICAgIHAxIDwtIGdncGxvdChkYXRhX2ZpbHQsIGFlcyh4ID0gZm9yY2F0czo6ZmN0X2luZnJlcShjb3VudHJ5KSksdGV4dCA9IG5hbWUpICsKICAgICAgZ2VvbV9iYXIoc3RhdCA9ICdjb3VudCcsIGNvbG9yID0gImJsYWNrIiwgZmlsbCA9ICJyZWQyIikgKwogICAgICBnZ3RpdGxlKCdXaGljaCBDb3VudHJpZXMgTWFrZSB0aGUgTW9zdCBNb3ZpZXM/JykgKwogICAgICBsYWJzKHggPSAnQ291bnRyeScpICsKICAgICAgbGFicyh5ID0gJ051bWJlciBvZiBNb3ZpZXMgUHJvZHVjZWQnKSsKICAgICAgdGhlbWVfY2xhc3NpYygpICsKICAgICAgZ2d0aGVtZXM6OnNjYWxlX2NvbG9yX3RhYmxlYXUoKSsKICAgICAgdGhlbWUoYXhpcy50ZXh0LnggPSBlbGVtZW50X3RleHQoYW5nbGUgPSA5MCwgdmp1c3QgPSAwLjUsIGhqdXN0PTEpKQogICAgZ2dwbG90bHkocDEpCiAgfQopCgoKIyBhcHAkcnVuX3NlcnZlcihob3N0ID0gJzAuMC4wLjAnKQphcHAkcnVuX3NlcnZlcihkZWJ1ZyA9IFQpICMgdXNlIHdoZW4gcnVubmluZyBsb2NhbGx5CmBgYAoKCmBgYHtyfQoKIyB3b3JrIG9uIApsaWJyYXJ5KGRhc2gpCmxpYnJhcnkoZGFzaENvcmVDb21wb25lbnRzKQpsaWJyYXJ5KGdncGxvdDIpCmxpYnJhcnkocGxvdGx5KQpsaWJyYXJ5KHB1cnJyKQogCiMgZHJvcCBkb3duIGxpc3QKZHJvcF9saXN0IDwtIGxpc3QoCiAgICAiVFYtRyIsCiAgICAiVFYtMTQiLAogICAgIlRWLU1BIiwKICAgICJUVi1QRyIsCiAgICAiUiIsCiAgICAiVFYtWTciLAogICAgIlRWLVkiLAogICAgIlBHIiwKICAgICJHIiwKICAgICJQRy0xMyIsCiAgICAiTlIiLAogICAgIlVSIiwKICAgICJUVi1ZNy1GViIsCiAgICAiTkMtMTciKQoKZGF0YSA9IHJlYWQuY3N2KGZpbGUgPSAiZGF0YS9wcm9jZXNzZWQvY2xlYW5fZGYuY3N2IikKCgphcHAgPC0gRGFzaCRuZXcoZXh0ZXJuYWxfc3R5bGVzaGVldHMgPSBkYmNUaGVtZXMkQk9PVFNUUkFQKQoKYXBwJGxheW91dCgKICBkYmNDb250YWluZXIoCiAgICBsaXN0KAogICAgICBkY2NHcmFwaChpZD0ncGxvdF9saW5lJyksCiAgICAgIGRjY0Ryb3Bkb3duKAogICAgICAgIGlkPSdyYXRpbmctc2VsZWN0JywKICAgICAgICBvcHRpb25zID0gZHJvcF9saXN0ICU+JSAgcHVycnI6Om1hcChmdW5jdGlvbihyYXRpbmcscG9wKSBsaXN0KGxhYmVsID0gcmF0aW5nLCB2YWx1ZSA9IHJhdGluZykpICwgCiAgICAgICAgdmFsdWU9bGlzdCgiVFYtRyIsIlRWLU1BIiwgIlRWLTE0IiwiVFYtWTciKSwKICAgICAgICBtdWx0aT1UUlVFKSwKICAgICAgZGNjUmFuZ2VTbGlkZXIoCiAgICAgICAgaWQ9J215LXJhbmdlLXNsaWRlcicsCiAgICAgICAgbWluPTE5NDIsCiAgICAgICAgbWF4PTIwMjAsCiAgICAgICAgbWFya3MgPSBsaXN0KAogICAgICAgICAgJzE5NDInID0gJzE5NDInLAogICAgICAgICAgJzE5NjAnID0gJzE5NjAnLAogICAgICAgICAgJzE5ODAnID0gJzE5ODAnLAogICAgICAgICAgJzIwMDAnID0gJzIwMDAnLAogICAgICAgICAgJzIwMjAnID0gJzIwMjAnCiAgICAgICAgICAKICAgICAgICApLAogICAgICAgIHZhbHVlPWxpc3QoMjAwMywgMjAyMCkKICAgICAgKQogICAgKQogICkKKQoKYXBwJGNhbGxiYWNrKAogIG91dHB1dCgncGxvdF9saW5lJywgJ2ZpZ3VyZScpLAogIGxpc3QoaW5wdXQoJ3JhdGluZy1zZWxlY3QnLCAndmFsdWUnKSwKICAgICAgIGlucHV0KCdteS1yYW5nZS1zbGlkZXInLCd2YWx1ZScpKSwKICBmdW5jdGlvbihyYXRpbmdzX3JhbmdlLHllYXJfcmFuZ2UpIHsKICAgIGRmIDwtIG5hLm9taXQoZGF0YSkgJT4lIAogICAgICBmaWx0ZXIocmVsZWFzZV95ZWFyID4geWVhcl9yYW5nZVsxXSxyZWxlYXNlX3llYXIgPCB5ZWFyX3JhbmdlWzJdKSAlPiUKICAgICAgZmlsdGVyKHJhdGluZyAlaW4lIHJhdGluZ3NfcmFuZ2UpICU+JQogICAgICBncm91cF9ieShyZWxlYXNlX3llYXIscmF0aW5nKSAlPiUgCiAgICAgIHN1bW1hcmlzZShjb3VudCA9IGxlbmd0aChyYXRpbmcpKQogICAgCiAgICBwbG90ICA8LSBnZ3Bsb3QoZGYgLGFlcyh4ID0gcmVsZWFzZV95ZWFyLCB5ID0gY291bnQsIGNvbG9yID0gcmF0aW5nKSkgKwogICAgICBnZW9tX2xpbmUoKSsgICAgICAKICAgICAgc2NhbGVfc2l6ZShyYW5nZSA9IGMoMiwgMTIpKSArCiAgICAgIGdndGl0bGUoJ01vdmllIHJhdGluZyBpbiBOZXRmbGl4IGluIGRpZmZlcmVudCB5ZWFycycpICsKICAgICAgbGFicyh4ID0gJ1llYXJzJywgeT0gIk51bWJlciBvZiBtb3ZpZSIpICsKICAgICAgdGhlbWVfYncoKSArCiAgICAgIHRoZW1lKHRleHQgPSAgZWxlbWVudF90ZXh0KHNpemUgPSAxMCkpICsKICAgICAgZ2d0aGVtZXM6OnNjYWxlX2NvbG9yX3RhYmxlYXUoKSAKICAgIAogICAgZ2dwbG90bHkocGxvdCkKICB9CikKCmFwcCRydW5fc2VydmVyKGRlYnVnID0gVCkgIyB1c2Ugd2hlbiBydW5uaW5nIGxvY2FsbHkKCmBgYAoKYGBge3J9Cgpkcm9wX2xpc3QgPC0gbGlzdCgKICAgICJUVi1HIiwKICAgICJUVi0xNCIsCiAgICAiVFYtTUEiLAogICAgIlRWLVBHIiwKICAgICJSIiwKICAgICJUVi1ZNyIsCiAgICAiVFYtWSIsCiAgICAiUEciLAogICAgIkciLAogICAgIlBHLTEzIiwKICAgICJOUiIsCiAgICAiVVIiLAogICAgIlRWLVk3LUZWIiwKICAgICJOQy0xNyIpCgpvcHRpb25zIDwtIGRyb3BfbGlzdCAlPiUgIHB1cnJyOjptYXAoZnVuY3Rpb24ocmF0aW5nLHBvcCkgbGlzdChsYWJlbCA9IHJhdGluZywgdmFsdWUgPSByYXRpbmcpKQoKb3B0aW9ucwojIHRlc3RpbmcgPC0gZGF0YSB8PgojICAgc2VsZWN0KHJhdGluZywgdGl0bGUpIHw+CiMgICBncm91cF9ieShyYXRpbmcpIAojIAojIAojIHN0cihkYXRhJHJhdGluZykKIyBkcm9wX2xpc3QgPC0gbGlzdCgiVFYtRyIsCiMgICAgICAgICAgICAgICAgICAgIlRWLTE0IiwKIyAgICAgICAgICAgICAgICAgICAiVFYtTUEiCiMgICAgICAgICAgICAgICAgICAgKQpgYGAKCg==